//CPU可寻址内存（64KB）
//CPU能寻址的所有地址空间，包括卡带上的空间
import {PPU} from './ppu.js'
import {FC} from './fc.js'
import {APU} from './apu.js'

export class Memory{
    constructor(fc){
        this.instance = null
        // 可寻址内存总空间，用数组表示，共64K 
        this.mem = new Array(0xFFFF)
        this.ppu = PPU.getInstance()
        this.apu = APU.getInstance()
        this.fc = fc
        // 初始化内存，全部写入0x00
        for(let i=0;i<this.mem.length;i++){
            this.mem[i] = 0x00
        }
    }

    static getInstance(fc){
        if(!this.instance){
            this.instance = new Memory(fc)
            return this.instance
        }
        else{
            return this.instance
        }
    }

    // 读取内存位置，返回8bit数据，默认返回0x00
    load(address){
        let data
        // 读PPU寄存器
        if(address >= 0x2000 && address <= 0x2007){
            switch(address){
                case 0x2000:
                    // PPU控制寄存器
                    // 只写寄存器，不能读取
                    break
                case 0x2002:
                    // PPU状态寄存器
                    data = this.ppu.readPPUSTATUS()
                    break
                case 0x2004:
                    // 读精灵RAM数据
                    data = this.ppu.spriteMemory.load(this.ppu.spriteMemory.localPointer)
                    this.ppu.spriteMemory.localPointer++
                    break
                case 0x2007:
                    data = this.ppu.readData()
                    break
                // case 0x2005:
                //     data = this.ppu.PPUSCROLL
                //     break
                default:
                    data = 0x00
            }
        }
        // 读APU状态寄存器
        else if(address == 0x4015){
            data = this.apu.readControlReg()
        }
        // 读手柄寄存器
        else if(address >= 0x4016 && address <= 0x4017){
            switch(address){
                // 读1P状态
                case 0x4016:
                    data = this.fc.controller1.readCurrentButton() & 1
                    break
                default:
                    data = 0x00
            }
        }
        // 读内存映射位置
        else{
            data = this.mem[address]
        }
        return data
    }

    // 写入内存位置
    write(address,data){
        // 写PPU寄存器
        if(address >= 0x2000 && address <= 0x2007){
            switch(address){
                case 0x2000:
                    // 写PPU控制寄存器
                    this.ppu.writePPUCTRL(data)
                    break
                case 0x2001:
                    break
                case 0x2002:
                    // PPU状态寄存器
                    // 只读寄存器，不能写
                    break
                case 0x2003:
                    // 精灵RAM指针
                    this.ppu.spriteMemory.localPointer = data
                    break
                case 0x2004:
                    // 写精灵RAM数据
                    this.ppu.spriteMemory.write(this.ppu.spriteMemory.localPointer,data)
                    this.ppu.spriteMemory.localPointer++
                    break
                case 0x2006:
                    this.ppu.writeVRAMAddress(data)
                    break
                case 0x2007:
                    // console.log('address is 2007')
                    this.ppu.writeVRAM(data)
                    break
                default:
            }
        }
        else if(address == 0x4014){
            let startDMAAddress = (data << 8) & 0xFF00
            // Fixed！ 20211230 DMA需要实现填充到FF地址，否则精灵渲染时最后一个精灵的x值会有问题，是255
            for(let i = 0; i <= 0xFF; i++){
                this.ppu.spriteMemory.write(i,this.mem[startDMAAddress + i])
            }
        }
        // 写APU寄存器(波形部分)
        else if(address >= 0x4000 && address <= 0x4013){
            this.apu.writeChannel(address,data)
        }
        // 写APU寄存器（状态寄存器4015）
        else if(address == 0x4015){
            this.apu.writeControlReg(data)
        }
        // 写APU寄存器（FrameCounter寄存器4017,只使用高2bit，低位用于手柄2读取）
        else if(address == 0x4017){
            this.apu.setFrameCounterStatus(data)
        }
        // 写手柄寄存器
        else if(address == 0x4016){
            this.fc.controller1.resetIndex()
        }
        // 写内存映射位置
        else{
            this.mem[address] = data
        }
    }

        // 只在mapper中调用，将rom内容映射到内存空间
        writeToMemory(romData,startAddress,endAddress){
            for(let i=0;i<romData.length;i++){
                this.mem[startAddress+i] = romData[i]
            }
        }
}